# -*- coding: utf-8 -*-
import re

import scrapy
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor

from painoScore.items import PianoScoreItem


# 钢琴谱-流行
class PianoPopularSpider(CrawlSpider):
    # 爬虫框架名字
    name = "pianoPopular"
    # 允许访问的域名
    allowed_domains = ["everyonepiano.cn"]

    # 起始URL设为登录后要访问的第一页（无需手动在parse中发送）,CrawlSpider的rules会自动提取其他分页和详情页链接
    start_urls = [
        "https://www.everyonepiano.cn/Music-class5.html?&p=1&canshu=cn_edittime&word=&author=&jianpu=&paixu=desc&username="]


    # 定义爬取规则(使用正则表达式定义爬取哪些格式的链接)
    rules = (
        # 爬取所有/wuxia/数字 形式的链接，继续跟进(follow=True)
        # Rule(LinkExtractor(
        #     allow=r'/wuxia/(68[6-9]|69\d)',  # 只匹配/wuxia/686 到/wuxia/699范围的链接
        #     # allow=r'/wuxia/68[6-7]',  # 只匹配/wuxia/686 到/wuxia/699范围的链接
        #     restrict_xpaths='//a[@class="thumbnail"]'  # 只从带有thumbnail类的a标签中提取链接
        # ), follow=True),

        # 规则1：提取分页链接并跟进（处理所有分页）
        Rule(LinkExtractor(
            # 转义?，匹配p=1、p=2等分页参数
            allow=r'/Music-class5\.html\?&p=\d+&canshu=cn_edittime&word=&author=&jianpu=&paixu=desc&username=',
            # 限制分页链接提取范围（根据页面结构调整xpath，定位分页按钮区域）
            restrict_xpaths='//div[@class="row EOPMusicIndexPage"]',
        ), follow=True),

        # 规则2：提取详情页链接并解析（下载PDF文件）
        Rule(LinkExtractor(
            allow=r'/Music-\d+.html',
        ), callback='parse_item', follow=False),
    )

    # 登录
    def start_requests(self):
        """
        定义爬虫的起始请求方法
        该方法用于发送登录请求到目标网站
        先发送登录请求，携带cookie后再访问起始页
        """
        url = 'https://www.everyonepiano.cn/Login/index'  # 登录页面的URL地址
        form_data = {
            'username': 'Ssyl',  # 登录用户名
            'password': '18567316357',  # 登录密码
            'submit': '1',
            'care_url': 'https://www.everyonepiano.cn/Myspace',
            'backsure': '',
            'backurl': '',
            '__hash__': 'f50fd4d722f5cdd28249de4287082733_490fc819947418d21c2ca011444f8978',
        }
        # 发送一个POST表单请求，并指定回调函数为after_login方法
        yield scrapy.FormRequest(url, formdata=form_data, callback=self.after_login)

    # 重定向URL
    def after_login(self, response):
        """解析登录后的重定向，跟进到Myspace页面验证登录状态"""
        # 从JavaScript重定向脚本中提取目标URL（登录后返回的数据：window.location.href="https://www.everyonepiano.cn/Myspace"）
        redirect_match = re.search(r'window\.location\.href="(.*?)"', response.text)
        if redirect_match:
            # .group(1)：返回正则中 ‌第一个捕获组‌ 的内容（即 (.*?) 匹配的部分）
            redirect_url = redirect_match.group(1)
            self.logger.info(f"登录后重定向到：{redirect_url}")
            # 发送请求到重定向URL，验证登录状态
            yield scrapy.Request(
                url=redirect_url,
                callback=self.verify_login,
                dont_filter=True  # 允许重复访问
            )
        else:
            self.logger.error("未找到登录后的重定向URL，登录失败！")

    # 个人空间
    def verify_login(self, response):
        """在Myspace页面验证登录状态"""
        # 检查页面中是否包含用户名（根据Myspace页面实际结构调整XPath）
        username = response.xpath('//p[@class="UserName"]/text()').get()

        if "Ssyl" in username:
            self.logger.info(f"登录成功！当前用户：{username}")
            # 遍历start_urls，发送请求（CrawlSpider会自动应用rules）
            for url in self.start_urls:
                yield scrapy.Request(url, dont_filter=True)  # dont_filter避免被过滤
        else:
            self.logger.error("登录失败，未在Myspace页面找到用户名！")

    # 详情页
    def parse_item(self, response):
        """解析详情页数据（根据实际需求提取字段）"""
        self.logger.info(f"解析详情页：{response.url}")

        # 解析提取数据信息
        # 标题
        title = (response.xpath('//h1/text()').get()).replace('钢琴谱：', '')

        # 编号
        score_num = int((response.xpath('//div[@class="EOPRTRight"]/text()').get()).replace('编号：', ''))

        # 歌手/作者
        singer = response.xpath('//a[@style="color:green;"]/text()').get()

        # 点击次数
        click_count = int(
            (response.xpath('//div[@ class="hidden-xs EOPReadInfoTxt"]/ul/li[4]/text()').get()).replace('点击次数：',
                                                                                                        ''))

        # 图片
        image_urls = response.xpath('//img[@class="EOPReadPic"]/@src').get()
        base_url = 'https://www.everyonepiano.cn'
        # 判断是否为相对路径（不以http://或https://开头）
        if not image_urls.startswith(('http://', 'https://')):
            # 处理可能的重复斜杠（如相对路径是'/uploads/...'，拼接后避免//）
            full_image_url = [base_url + image_urls if image_urls.startswith('/') else f"{base_url}/{image_urls}"]
        else:
            full_image_url = [image_urls]

        # 简介
        description = response.xpath('//div[@id="MusicInfoTxt2"]//text()').extract()
        description_lst = []
        for desc in description:
            # 去除首尾的空白字符
            des_stripped = desc.strip()
            # 只打印包含非空白字符的内容
            if des_stripped:
                description_lst.append(des_stripped)
        full_description = ''.join(description_lst).replace(
            '如果该谱子不是您想要的感觉或缺少您想要的文件、歌词，我们也可以提供付费定制曲谱服务，点击了解详情。',
            '').replace('\n', '')

        # 创建item
        item = PianoScoreItem()

        # 给 item 赋值
        item['title'] = title
        item['score_num'] = score_num
        item['singer'] = singer
        item['click_count'] = click_count
        item['image_url'] = full_image_url[0]  # 保留原始URL
        item['image_urls'] = full_image_url  # 框架要求为列表
        item['description'] = full_description
        # print(item)

        # PDF链接
        pdf_links = response.xpath('//a[@title="PDF文件下载"]/@href').get()
        base_url = 'https://www.everyonepiano.cn'
        # 判断是否为相对路径（不以http://或https://开头）
        if not pdf_links.startswith(('http://', 'https://')):
            # 处理可能的重复斜杠（如相对路径是'/uploads/...'，拼接后避免//）
            full_pdf_links = [base_url + pdf_links if pdf_links.startswith('/') else f"{base_url}/{pdf_links}"]
        else:
            full_pdf_links = [pdf_links]

        # 用 meta={'item': item} 传递 item 到 parse2
        yield scrapy.Request(
            url=full_pdf_links[0],
            callback=self.pdf_parse_item,
            meta={'item': item}  # 关键：将当前 item 传递给下一个解析函数
        )

    # PDF下载页
    def pdf_parse_item(self, response):
        # 从 response.meta 中获取传递过来的 item
        item = response.meta['item']  # 这里的 item 就是 parse_item 中创建的对象

        # PDF文件下载页面的URL
        pdf_file_urls = response.xpath('//a[@class="btn btn-success btn-block BtnDown"][1]/@href').get()
        base_url = 'https://www.everyonepiano.cn'
        # 判断是否为相对路径（不以http://或https://开头）
        if not pdf_file_urls.startswith(('http://', 'https://')):
            # 处理可能的重复斜杠（如相对路径是'/uploads/...'，拼接后避免//）
            full_pdf_file_urls = [
                base_url + pdf_file_urls if pdf_file_urls.startswith('/') else f"{base_url}/{pdf_file_urls}"]
        else:
            full_pdf_file_urls = [pdf_file_urls]

        # 给 item 补充 PDF 相关字段
        item['file_url'] = full_pdf_file_urls[0]  # 存储所有 PDF URL（或取第一个）
        item['file_urls'] = full_pdf_file_urls  # 给 FilesPipeline 用的列表

        # 返回完整的 item（交给 Pipeline 处理，图片和PDF由框架自动下载）
        yield item
